#include "common_Socket.h"
#include <fstream>
#include <sstream>
#include <iostream>
void Socket::Protocolo(std::string& str)
{
    const char sep = '\n';
    str.push_back( sep );
}

void Socket::InversaProtocolo(std::string& str)
{
    size_t pos = str.rfind('\n');
    if (  pos != (str.size()-1) ){throw MyException("Protocolo Incorrecto");}
    str.erase(pos,1);
}

    //tira 2 excepciones => se cerro la conexion O error fatal en send
void Socket::SendData(const std::string& Data)
{
    std::string DataCopy = Data;
    this->Protocolo(DataCopy);
    const char* P = DataCopy.c_str();
    int BytesEnviados = 0;
    int Longitud = DataCopy.length();
    bool OK = true;

    while( (BytesEnviados < Longitud) && OK )
    {
        int CantEnvAux = send( this->NumSocket
                            , P+BytesEnviados
                            , Longitud-BytesEnviados
                            , 0 );
        if ( CantEnvAux == 0 )
        {
            throw MyException("Se cerro la conexión desde el otro lado");
        }
        if ( CantEnvAux == -1 )
        {
            throw MyException("Error fatal en send" + std::string(strerror(errno)));
        }

        if (OK){ BytesEnviados += CantEnvAux ; }
    }
/*    if(OK)
    {
        std::cout << "Enviados Satisfactoriamente:" <<
                        BytesEnviados << std::endl;
    }
*/
}

// INTENTA obtene msj de RAW
// trabaja hasta que : faltan bytes para completar uno
bool Socket::ProcesarCrudo(std::string& MensRec)
{
    bool MsjEncontrado = false ;
    size_t pos = this->RAW.rfind('\n');
    if (  pos != std::string::npos )
    {
        MsjEncontrado = true;
        std::string MsjProt = this->RAW.substr(0,pos+1);
        this->InversaProtocolo(MsjProt);
        MensRec = MsjProt;
        this->RAW.erase(0,pos+2); //NUll terminated
    }
    return MsjEncontrado;
}


int Socket::Select(int Segundos)
{
    fd_set readfds;
    FD_ZERO( &readfds );
    FD_SET( this->NumSocket , &readfds);
    struct timeval tv;
    tv.tv_sec = Segundos;
    tv.tv_usec = 0;
    int retval = select( this->NumSocket+1 ,&readfds, NULL, NULL, &tv);
    return retval;
}

    //puede tirar 3 excepciones
    // se cerro la conexion recv
    // error fatal en recv
    // error en protocolo
void Socket::ReciveData( ColaThreadSafe<Mensaje>& Cola )
{
    int BYTES_RECIBIDOS = 0;
    bool OK = true ;
    bool recibido = false;
    std::string MensajeRecibido="";    //aca va el msj
    //Verifico que mi buffer Raw no contiene un msj!
    recibido = this->ProcesarCrudo(MensajeRecibido);
    if ( recibido ){ OK = false ; }
    while( (OK) )
    {
        char Buff[MAX_BUFF+1] ;
        memset(Buff, '\0' , MAX_BUFF+1) ;
        int CantRecAux = recv( this->NumSocket, Buff, MAX_BUFF+1 , 0 );
//************************************************
//        std::cout << Buff << std::endl;
//        std::cout << "+++++++++++++++++++++++++++" << std::endl;
//************************************************
        //verifico estado de conexion
        if ( CantRecAux == 0 )
        {
            throw MyException("Se cerro la conexión desde el otro lado en recv."
                             +std::string(strerror(errno)) );
        }
        if ( CantRecAux == -1 )
        {
        throw MyException("Error fatal en recv."+std::string(strerror(errno)));
        }
        BYTES_RECIBIDOS += CantRecAux;
        //****************************
        this->RAW.append(Buff,CantRecAux);   //junto los datos a ser procesados
        //std::cout << this->RAW << std::endl;
        recibido = this->ProcesarCrudo(MensajeRecibido);
        if ( recibido ){OK = false;}
    }

    if ( recibido == true ){
        struct sockaddr_in Info;
        this->GetInfoDelPeer(Info);
        //std::cout << MensajeRecibido.size() << std::endl;
        Cola.push(Mensaje(MensajeRecibido,Info));
        //std::cout << "Bytes Recibidos:"<< BYTES_RECIBIDOS << std::endl;
    }

}

void Socket::Inicializar()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        throw MyException("Error fatal,no se pudo crear Socket");
    }
    //con este numero voy a llamar a las librerias de C de socket
    this->NumSocket = sockfd;
    this->RAW = "";
}

void Socket::ShutDown()
{
    int ret = shutdown( this->NumSocket , SHUT_RDWR );
    if ( ret == -1 )
    {
        throw MyException("Error en Shut Down."+std::string(strerror(errno)));
    }
}

Socket::~Socket(){}
